﻿#include "ChengguRSUClient.h"
#include <cstring>
#include <iconv.h>
#include <memory>
#include <sstream>
#include <string>
#include "KafkaClient.h"
#include "spdlog/fmt/bundled/format.h"
#include "spdlog/spdlog.h"

void ChengGuRsuClient::Init(std::shared_ptr<DeviceInfo> info) {
    _device_info = info;
    _client = std::make_shared<TcpClient>();
    _client->Init(info->IP, info->Port);
    _client->SetAutoReconnect(true, 5);

    _client->SetConnectedCallback(std::bind(&ChengGuRsuClient::OnConnected, this));
    _client->SetRcvDataCallback(std::bind(&ChengGuRsuClient::OnReceiveData, this, std::placeholders::_1));
}

void ChengGuRsuClient::Start() {
    if (_client) {
        _client->Connect();
    }
}

void ChengGuRsuClient::Stop() {
    if (_client) {
        _client->Disconnect();
    }
}

/// <summary>
/// 连接上时发送初始化命令
/// </summary>
void ChengGuRsuClient::OnConnected() {

    auto len = sizeof(DataFrameHeader) + sizeof(MEC_RsuInitFrame) + 2;
    auto data = std::shared_ptr<uint8_t>(new uint8_t[len], [](uint8_t *ptr) { delete[] ptr; });

    //数据头部
    DataFrameHeader frame_header;
    frame_header.SEQ = _seq_num.get();
    //高两个字节保留，低两个字节表示DATA域的长度
    frame_header.LEN_L = htons(sizeof(MEC_RsuInitFrame));
    memcpy(data.get(), &frame_header, sizeof(DataFrameHeader));

    //数据内容
    MEC_RsuInitFrame rsu_init_frame;
    rsu_init_frame.CmdType = 0xC0;
    uint32_t timestamp = static_cast<uint32_t>(std::time(NULL));
    // rsu_init_frame.UnixTime = (timestamp);
    rsu_init_frame.UnixTime = htonl(timestamp);
    rsu_init_frame.PLLChannelID = 0;
    memcpy(data.get() + sizeof(DataFrameHeader), &rsu_init_frame, sizeof(MEC_RsuInitFrame));

    std::stringstream sss;
    for (int i = 2; i < len - 2; i++) {
        sss << fmt::format("{:02X} ", data.get()[i]);
    }
    // SPDLOG_INFO(sss.str());

    //校验位置
    uint16_t crc = htons(CRC16_X25(data.get() + 2, len - 4));
    memcpy(data.get() + sizeof(DataFrameHeader) + sizeof(MEC_RsuInitFrame), &crc, sizeof(uint16_t));

    std::stringstream ss;
    for (int i = 0; i < len; i++) {
        ss << fmt::format("{:02X} ", data.get()[i]);
    }

    _client->SendData((char *)data.get(), len);
}

void ChengGuRsuClient::OnReceiveData(const toolkit::Buffer::Ptr &buffer) {
    std::stringstream ss;
    for (int i = 0; i < buffer->size(); i++) {
        ss << fmt::format("{:02X} ", buffer->data()[i]);
    }
    // SPDLOG_INFO("rcv-->  " + ss.str());
    _buffer.append(buffer->data(), buffer->size());

    ParseData();
}

void ChengGuRsuClient::ParseData() {
    int pos = -1;
    auto len = _buffer.size();

    for (size_t i = 0; i < len - i; i++) {
        if ((uint8_t)_buffer[i] == 0xFF && (uint8_t)_buffer[i + 1] == 0xFF) {
            pos = i;
            break;
        }
    }

    if (pos < 0)
        return;

    if (pos > 0)
        _buffer.erase(0, pos);

    //第8个字节为CmdType
    uint8_t flag = static_cast<uint8_t>(_buffer[8]);
    switch (flag) {
        case 0xB0: //设备状态
        {
            RSU_DeviceStatusFrame frame;
            memcpy(&frame, _buffer.c_str() + 8, sizeof(RSU_DeviceStatusFrame));
            _buffer.erase(0, sizeof(RSU_DeviceStatusFrame) + 10);

            auto rsuid = fmt::format("{:02X} {:02X} {:02X}", frame.RSUID[0], frame.RSUID[1], frame.RSUID[2]);

            nlohmann::json doc
                = { { "rsuCtlIp", _device_info->IP },
                    { "rsuCtlPort", std::to_string(_device_info->Port) },
                    { "vendorName", "jy" },
                    { "rsuNo", rsuid },
                    { "rsuCtlNo", "1" },
                    { "storage", 0.0 },
                    { "dataTime", fmt::format("{:%Y-%m-%d %H:%M:%S}", fmt::localtime(std::time(nullptr))) },
                    { "rsuStatus", 1 },
                    { "ctlHardwareNo", " " },
                    { "ctlSoftwareNo", std::to_string(frame.RSUVersion) },
                    { "headHardwareNo", " " },
                    { "headSoftwareNo", " " },
                    { "temperature", 0.0 },
                    { "mbNetStatus", 1 },
                    { "rsuHeadNum", 0 },
                    { "psamNum", 0 } };
            KafkaClient::GetInstance()->Publish(doc.dump(4));
        } break;
        case 0xBB: //心跳
        {
            int reserve1 = _buffer.length();

            RSU_HeartbeatFrame frame;
            memcpy(&frame, _buffer.c_str() + 8, sizeof(RSU_HeartbeatFrame));
            auto size = sizeof(RSU_HeartbeatFrame);
            _buffer.erase(0, size + 10);
            int reserve2 = _buffer.length();

            auto len = sizeof(DataFrameHeader) + sizeof(MEC_HeartbeatFrame) + 2;
            auto data = std::shared_ptr<uint8_t>(new uint8_t[len], [](uint8_t *ptr) { delete[] ptr; });

            //数据头部
            DataFrameHeader frame_header;
            frame_header.SEQ = _seq_num.get();
            //高两个字节保留，低两个字节表示DATA域的长度
            frame_header.LEN_L = htons(1);
            memcpy(data.get(), &frame_header, sizeof(DataFrameHeader));

            //数据内容
            MEC_HeartbeatFrame heartbeat;
            memcpy(data.get() + sizeof(DataFrameHeader), &heartbeat, sizeof(MEC_HeartbeatFrame));

            //校验位置
            uint16_t crc = htons(CRC16_X25(data.get() + 2, len - 4));
            memcpy(data.get() + sizeof(DataFrameHeader) + sizeof(MEC_HeartbeatFrame), &crc, sizeof(uint16_t));

            _client->SendData((char *)data.get(), len);

            if (!_send_init_frame) {
                _send_init_frame = true;
                OnConnected();
            }

        } break;
        case 0xB4: //车辆信息
        {
            RSU_VehicleInformationFrame frame;
            memcpy(&frame, _buffer.c_str() + 8, sizeof(RSU_VehicleInformationFrame));
            _buffer.erase(0, sizeof(RSU_VehicleInformationFrame) + 10);
            auto plate = std::string((char *)frame.VehiclePlate);

            SPDLOG_INFO(
                "车辆信息 ---> 车牌:{}   车型:{}   颜色:{}   RSU:{}", convert_gb2312_to_utf8(plate),
                vehicle_type_map[frame.VehicleType], color_map[frame.Colour], _device_info->IP);

            //收到信息后，发送确认
            auto len = sizeof(DataFrameHeader) + sizeof(MEC_AckFrame) + 2;
            auto data = std::shared_ptr<uint8_t>(new uint8_t[len], [](uint8_t *ptr) { delete[] ptr; });

            //数据头部
            DataFrameHeader frame_header;
            frame_header.SEQ = _seq_num.get();
            //高两个字节保留，低两个字节表示DATA域的长度
            frame_header.LEN_L = htons(sizeof(MEC_AckFrame));
            memcpy(data.get(), &frame_header, sizeof(DataFrameHeader));

            //数据内容
            MEC_AckFrame ack;
            memcpy(data.get() + sizeof(DataFrameHeader), &ack, sizeof(MEC_AckFrame));

            //校验位置
            uint16_t crc = htons(CRC16_X25(data.get() + 2, len - 4));
            memcpy(data.get() + sizeof(DataFrameHeader) + sizeof(MEC_AckFrame), &crc, sizeof(uint16_t));

            _client->SendData((char *)data.get(), len);
        } break;
        default: break;
    }

    if (_buffer.length() > 6) {
        ParseData();
    }
}

uint16_t CRC16_X25(uint8_t *data, uint16_t len) {
    uint16_t crc = 0xFFFF; // 初始化CRC值为0xFFFF

    for (int i = 0; i < len; i++) {
        crc ^= data[i];
        for (int i = 0; i < 8; ++i) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0x8408;
            } else {
                crc >>= 1;
            }
        }
    }

    // 返回CRC值
    return ~crc & 0xFFFF;
}

std::string convert_gb2312_to_utf8(const std::string &input) {
    iconv_t cd = iconv_open("UTF-8", "GB2312"); // 打开GB2312到UTF-8的转换

    if (cd == (iconv_t)(-1)) {
        std::cerr << "iconv_open failed!" << std::endl;
        return "";
    }

    size_t in_len = input.size();
    size_t out_len = in_len * 2; // UTF-8比GB2312占用的字节数要多

    char *in_buf = const_cast<char *>(input.c_str());
    char *out_buf = (char *)malloc(out_len);
    char *out_ptr = out_buf;

    // 使用iconv进行转换
    if (iconv(cd, &in_buf, &in_len, &out_ptr, &out_len) == (size_t)(-1)) {
        std::cerr << "iconv conversion failed!" << std::endl;
        iconv_close(cd);
        free(out_buf);
        return "";
    }

    std::string result(out_buf, out_ptr); // 转换后的UTF-8字符串
    iconv_close(cd); // 关闭iconv转换器
    free(out_buf); // 释放内存
    return result;
}
